#pragma once

#include "Common.h"

template <class TYPE, class OBJECT_TYPE> class Reference;

template <class TYPE, class OBJECT_TYPE>
class ReferenceObject
{
public:
	CONSTRUCTOR ReferenceObject();
	CONSTRUCTOR ReferenceObject(const ReferenceObject<TYPE, OBJECT_TYPE>& CopyObject);
	DESTRUCTOR ~ReferenceObject();

	const ReferenceObject<TYPE, OBJECT_TYPE>& operator =(const ReferenceObject<TYPE, OBJECT_TYPE>& CopyObject);

	void Clear();

	void SetData(OBJECT_TYPE NewData);
	OBJECT_TYPE GetData() const;

	Reference<TYPE, OBJECT_TYPE>* GetFirstReference() const;
	Reference<TYPE, OBJECT_TYPE>* GetLastReference() const;
	Reference<TYPE, OBJECT_TYPE>* GetNextReference(Reference<TYPE, OBJECT_TYPE>* Reference) const;
	Reference<TYPE, OBJECT_TYPE>* GetPreviousReference(Reference<TYPE, OBJECT_TYPE>* Reference) const;

	u32 GetReferenceCount() const;

protected:
	void Add(Reference<TYPE, OBJECT_TYPE>* Reference);
	void Remove(Reference<TYPE, OBJECT_TYPE>* Reference);

	Reference<TYPE, OBJECT_TYPE>* FirstReference;
	Reference<TYPE, OBJECT_TYPE>* LastReference;

	u32 ReferenceCount;

	OBJECT_TYPE Data;

	friend class Reference<TYPE, OBJECT_TYPE>;
};

/// Post included files
#include "Reference.h"


/// Constructor
template <class TYPE, class OBJECT_TYPE>
ReferenceObject<TYPE, OBJECT_TYPE>::ReferenceObject()
{
	FirstReference = NULL;
	LastReference = NULL;

	ReferenceCount = 0;
}


/// Copy constructor
template <class TYPE, class OBJECT_TYPE>
ReferenceObject<TYPE, OBJECT_TYPE>::ReferenceObject(const ReferenceObject<TYPE, OBJECT_TYPE>& CopyObject)
{
	FirstReference = NULL;
	LastReference = NULL;

	ReferenceCount = 0;
}


/// Destructor
template <class TYPE, class OBJECT_TYPE>
ReferenceObject<TYPE, OBJECT_TYPE>::~ReferenceObject()
{
	Clear();
}


/// Assignment operator
template <class TYPE, class OBJECT_TYPE>
const ReferenceObject<TYPE, OBJECT_TYPE>& ReferenceObject<TYPE, OBJECT_TYPE>::operator =(const ReferenceObject<TYPE, OBJECT_TYPE>& CopyObject)
{
	FirstReference = NULL;
	LastReference = NULL;

	ReferenceCount = 0;

	return (*this);
}


/// Clears all attached references
template <class TYPE, class OBJECT_TYPE>
void ReferenceObject<TYPE, OBJECT_TYPE>::Clear()
{
	Reference<TYPE, OBJECT_TYPE>* CurrentReference;
	Reference<TYPE, OBJECT_TYPE>* NextReference;

	CurrentReference = FirstReference;
	while(CurrentReference != NULL)
	{
		NextReference = CurrentReference->NextReference;

		CurrentReference->mReferenceObject = NULL;
		CurrentReference->NextReference = NULL;
		CurrentReference->PreviousReference = NULL;

		CurrentReference = NextReference;
	}

	FirstReference = NULL;
	LastReference = NULL;

	ReferenceCount = 0;
}


/// Sets a new data
template <class TYPE, class OBJECT_TYPE>
void ReferenceObject<TYPE, OBJECT_TYPE>::SetData(OBJECT_TYPE NewData)
{
	Data = NewData;
}


/// Returns the data
template <class TYPE, class OBJECT_TYPE>
OBJECT_TYPE ReferenceObject<TYPE, OBJECT_TYPE>::GetData() const
{
	return Data;
}


/// Returns the first reference
template <class TYPE, class OBJECT_TYPE>
Reference<TYPE, OBJECT_TYPE>* ReferenceObject<TYPE, OBJECT_TYPE>::GetFirstReference() const
{
	return FirstReference;
}


/// Returns the last reference
template <class TYPE, class OBJECT_TYPE>
Reference<TYPE, OBJECT_TYPE>* ReferenceObject<TYPE, OBJECT_TYPE>::GetLastReference() const
{
	return LastReference;
}


/// Returns the next reference
template <class TYPE, class OBJECT_TYPE>
Reference<TYPE, OBJECT_TYPE>* ReferenceObject<TYPE, OBJECT_TYPE>::GetNextReference(Reference<TYPE, OBJECT_TYPE>* Reference) const
{
	if(Reference == NULL) return NULL;

	return Reference->NextReference;
}


/// Returns the previous reference
template <class TYPE, class OBJECT_TYPE>
Reference<TYPE, OBJECT_TYPE>* ReferenceObject<TYPE, OBJECT_TYPE>::GetPreviousReference(Reference<TYPE, OBJECT_TYPE>* Reference) const
{
	if(Reference == NULL) return NULL;

	return Reference->PreviousReference;
}


/// Returns the reference counter
template <class TYPE, class OBJECT_TYPE>
u32 ReferenceObject<TYPE, OBJECT_TYPE>::GetReferenceCount() const
{
	return ReferenceCount;
}


/// Adds a reference to the reference object
template <class TYPE, class OBJECT_TYPE>
void ReferenceObject<TYPE, OBJECT_TYPE>::Add(Reference<TYPE, OBJECT_TYPE>* Reference)
{
	Reference->NextReference = NULL;
	Reference->PreviousReference = LastReference;

	if(FirstReference == NULL) FirstReference = Reference;
	if(LastReference != NULL) LastReference->NextReference = Reference;
	LastReference = Reference;

	ReferenceCount++;
}


/// Removes a reference from the reference object
template <class TYPE, class OBJECT_TYPE>
void ReferenceObject<TYPE, OBJECT_TYPE>::Remove(Reference<TYPE, OBJECT_TYPE>* Reference)
{
	if(Reference->NextReference == NULL)
	{
		if(Reference->PreviousReference == NULL)
		{
			FirstReference = NULL;
			LastReference = NULL;
		}
		else
		{
			LastReference = Reference->PreviousReference;
			LastReference->NextReference = NULL;
		}
	}
	else
	{
		if(Reference->PreviousReference == NULL)
		{
			FirstReference = Reference->NextReference;
			FirstReference->PreviousReference = NULL;
		}
		else
		{
			Reference->PreviousReference->NextReference = Reference->NextReference;
			Reference->NextReference->PreviousReference = Reference->PreviousReference;
		}
	}

	ReferenceCount--;
}